package pl.wroc.pwr.docs.repos.webapp.repo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.DublinCore;
import org.apache.tika.exception.TikaException;

import pl.wroc.pwr.docs.repos.webapp.repo.Document;
import pl.wroc.pwr.docs.repos.webapp.repo.Document.FORMAT;
import pl.wroc.pwr.docs.repos.webapp.repo.Document.STORAGE_TYPE;

// TODO dodać loggera 

public class DocumentDao {
	private Connection con;
	
	public DocumentDao(Connection con) {
		this.con = con;
	}
	
	public int addDocument(String filename, InputStream in) throws SQLException, IOException, TikaException, Exception {
		FORMAT format = Document.format(in);
		Date date = new Date();
		java.sql.Timestamp sqlDate = new java.sql.Timestamp(date.getTime());
		
		String query = "INSERT INTO documents(filename, format, type, added, binary)" +
                       "VALUES(?, ?, ?, ?, ?)";
		
		PreparedStatement pstmt = con.prepareStatement(query,Statement.RETURN_GENERATED_KEYS);
		pstmt.setString(1, filename);
		pstmt.setString(2, format.name());
		pstmt.setString(3, STORAGE_TYPE.DB_FILE.name());
		pstmt.setObject(4, sqlDate);
		pstmt.setBlob(5, in);
		
		pstmt.executeUpdate();
		ResultSet rs = pstmt.getGeneratedKeys();
		rs.next();
		return rs.getInt(1);
	}
	
	public int addDocument(String filename, InputStream in, String filepath) throws SQLException, IOException, TikaException {
		FORMAT format = Document.format(in);
		Date date = new Date();
		java.sql.Timestamp sqlDate = new java.sql.Timestamp(date.getTime());

		String query = "INSERT INTO documents(filename, format, type, added, filepath)" +
                       "VALUES(?, ?, ?, ?, ?)";
		
		PreparedStatement pstmt = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
		pstmt.setString(1, filename);
		pstmt.setString(2, format.name());
		pstmt.setString(3, STORAGE_TYPE.FLAT_FILE.name());
		pstmt.setObject(4, sqlDate);
		pstmt.setString(5, filepath);
		
		pstmt.executeUpdate();
		ResultSet rs = pstmt.getGeneratedKeys();
		rs.next();
		return rs.getInt(1);
	}
	
	public void deleteDocument(int id) throws SQLException {
		String query = "DELETE FROM documents where id = ?;";
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setInt(1, id);
		pstmt.executeUpdate();
		
	}
	
	public String getContent(Document doc) throws SQLException, IOException, TikaException {
		return getContent(doc.getId());
	}
	
	public String getContent(int id) throws SQLException, IOException, TikaException{
		return getContent(id, -1);
	}
	
	public String getContent(int id, int n) throws SQLException, IOException, TikaException {
		String result = null;
		String query = "select * from documents where id = ?;";
		
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setInt(1, id);
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			InputStream in = null;
			STORAGE_TYPE type = STORAGE_TYPE.valueOf(rs.getString("type"));
			switch(type){
				case FLAT_FILE:
					String filepath = rs.getString("filepath");
					File file = new File(filepath);
					in = new FileInputStream(file);
					break;
				case DB_FILE:
					in = rs.getBinaryStream("binary");
					break;
			}
			
			result = Document.extractContent(in, n);
		}
		
		return result;
	}
	
	public void addIndexedContent(int id, String content) throws SQLException {
		String query = "update documents set indexed_content = ? where id = ?;";
		
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setString(1, content);
		pstmt.setInt(2, id);
		pstmt.executeUpdate();
	}
	
	public String getIndexedContent(int id) throws SQLException{
		String result = null;
		String query = "select indexed_content from documents where id = ?;";
		
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setInt(1, id);
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			result = rs.getString("indexed_content");
		}
		
		return result;
	}
	
	public Metadata getMetadata(int id) throws SQLException, IOException, TikaException, Exception {
		Metadata metadata = null;
		String query = "select * from documents where id = ?;";
		
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setInt(1, id);
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			InputStream in = null;
			STORAGE_TYPE type = STORAGE_TYPE.valueOf(rs.getString("type"));
			switch(type){
				case FLAT_FILE:
					String filepath = rs.getString("filepath");
					File file = new File(filepath);
					in = new FileInputStream(file);
					break;
				case DB_FILE:
					in = rs.getBinaryStream("binary");
					break;
			}
			metadata=Document.extractMetadata(in);
		}

		return metadata;
	}
	
	public void addMetadata(int id, Metadata metadata) throws SQLException, Exception {		
		if(metadata.get(DublinCore.CREATED) != null)
			addMetaDate(id, metadata);
		if(metadata.get(DublinCore.CREATOR) != null)
			addMetaAuthor(id, metadata);
		if(metadata.get(DublinCore.TITLE) != null)
			addMetaTitle(id, metadata);
	}
	
	public void addMetaDate(int id, Metadata metadata) throws SQLException{
		Date date=metadata.getDate(DublinCore.CREATED);
        java.sql.Timestamp sqlDate = new java.sql.Timestamp(date.getTime());
        
        String query = "update documents set created = ? where id = ?;";
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setObject(1, sqlDate);
		pstmt.setInt(2, id);
		pstmt.executeUpdate();
	}
	
	public void addMetaAuthor(int id, Metadata metadata) throws SQLException{
		String author=metadata.get(DublinCore.CREATOR).toString();
        
        String query = "update documents set author = ? where id = ?;";
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setString(1, author);
		pstmt.setInt(2, id);
		pstmt.executeUpdate();
	}
	
	public void addMetaTitle(int id, Metadata metadata) throws SQLException{
		String title=metadata.get(DublinCore.TITLE).toString();
        
        String query = "update documents set title = ? where id = ?;";
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setString(1, title);
		pstmt.setInt(2, id);
		pstmt.executeUpdate();
	}
	
	public Document getDocument(int id) throws SQLException {
		Document document = null;
		String query = "select * from documents where id = ?;";
		
		PreparedStatement pstmt = con.prepareStatement(query);
		pstmt.setInt(1, id);
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			STORAGE_TYPE type = STORAGE_TYPE.valueOf(rs.getString("type"));
			FORMAT format = FORMAT.valueOf(rs.getString("format"));
			String filename = rs.getString("filename");
			document = new Document(id, filename, format, type);
		}

		return document;
	}
	
	public Document getDocument(String lucene) throws SQLException {
		Document document = null;
		String query = "select id, type, format, filename from " + lucene; //TODO nie może tak zostać
		
		PreparedStatement pstmt = con.prepareStatement(query);
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			STORAGE_TYPE type = STORAGE_TYPE.valueOf(rs.getString("type"));
			FORMAT format = FORMAT.valueOf(rs.getString("format"));
			String filename = rs.getString("filename");
			int id = rs.getInt("id");
			document = new Document(id, filename, format, type);
		}

		return document;
	}
	
	public List<Document> getDocuments() throws SQLException {
		ArrayList<Document> result = new ArrayList<Document>();

		String query = "select * from documents;";
		
		Statement stmt = con.createStatement();
		ResultSet rs = stmt.executeQuery(query);
		
		while(rs.next()){
			STORAGE_TYPE type = STORAGE_TYPE.valueOf(rs.getString("type"));
			FORMAT format = FORMAT.valueOf(rs.getString("format"));
			String filename = rs.getString("filename");
			int id = rs.getInt("id");
			result.add(new Document(id, filename, format, type));
		}
		return result;
	}
}